package m.common.model.util;

import m.common.model.Model;
import m.system.exception.MException;
import m.system.util.*;
import manage.model.AdminLogin;

import java.util.*;
import java.util.regex.Pattern;

/**
 * 用户Model实例的检测，可以根据oid实时查询，也可以传入实例
 */
public class ModelQueryCheck<T extends Model> {
    /**
     * 初始化查询检查实例  根据clazz和oid查找最新的进行判断   doCheck方法可执行检查
     * 也可使用getModel方法，并且返回模型
     */
    public static <T extends Model> ModelQueryCheck<T> init(Class<T> clazz,String oid){
        return new ModelQueryCheck<>(clazz,oid);
    }

    /**
     * 初始化查询检查实例  使用提供的model判断  doCheck方法可执行检查
     */
    public static <T extends Model> ModelQueryCheck<T> init(T model){
        return new ModelQueryCheck<>((Class<T>) model.getClass(), model);
    }


    private final String oid;
    private final Set<String> fieldSet;
    private final List<Object[]> checkList;
    private final Class<T> clazz;
    private T model;

    private ModelQueryCheck(Class<T> clazz, T model){
        this(clazz,model.getOid());
        this.model=model;
    }
    private ModelQueryCheck(Class<T> clazz, String oid){
        this.clazz = clazz;
        this.oid=oid;
        this.fieldSet=new HashSet<>();
        this.checkList=new ArrayList<>();
    }

    /**不为空  否则errorMessage*/
    public ModelQueryCheck<T> checkNotNull(String field,String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"notNull","",errorMessage});
        return this;
    }
    /** 等于value 不推荐数组field  否则errorMessage*/
    public ModelQueryCheck<T> checkStrEq(String field, String value, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strEq",value,errorMessage});
        return this;
    }
    /** fields等于values 数量要相等 不推荐数组field  否则errorMessage*/
    public ModelQueryCheck<T> checkStrAllEq(String[] fields, String[] values, String errorMessage){
        fieldSet.addAll(Arrays.asList(fields));
        checkList.add(new Object[]{fields,"strEq",values,errorMessage});
        return this;
    }
    /**包含value 不推荐数组field  否则errorMessage*/
    public ModelQueryCheck<T> checkStrLike(String field, String value, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strLike",value,errorMessage});
        return this;
    }
    /**包含values 有一个value满足即可  不推荐数组field  否则errorMessage*/
    public ModelQueryCheck<T> checkStrLike(String field, String[] values, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strLike",values,errorMessage});
        return this;
    }
    /**field在value中 field为数组时使用checkStrAllIn方法  不推荐数组field 否则errorMessage*/
    public ModelQueryCheck<T> checkStrIn(String field, String[] value, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strIn",value,errorMessage});
        return this;
    }
    /**field在value中 如果field是数组，都需要再value中  否则errorMessage*/
    public ModelQueryCheck<T> checkStrAllIn(String field, String[] value, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strAllIn",value,errorMessage});
        return this;
    }
    /**fields在value中 有一个field满足即可  不推荐数组field 否则errorMessage*/
    public ModelQueryCheck<T> checkStrIn(String[] fields, String[] value, String errorMessage){
        fieldSet.addAll(Arrays.asList(fields));
        checkList.add(new Object[]{fields,"strIn",value,errorMessage});
        return this;
    }

    /**field的长度在min和max之间，含min和max， min和max为空则不判断  不推荐数组field*/
    public ModelQueryCheck<T> checkStrLenRange(String field,Integer min,Integer max,String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strLenRange",new Integer[]{min,max},errorMessage});
        return this;
    }
    /**field的值符合regexp正则表达式 如果field为数组，都需要满足正则表达式 否则errorMessage*/
    public ModelQueryCheck<T> checkStrRegexp(String field,String regexp,String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"strRegexp",regexp,errorMessage});
        return this;
    }
    /** 等于value (格式化)  否则errorMessage*/
    public ModelQueryCheck<T> checkDateEq(String field, Date value,String format, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"dateEq",value,errorMessage,format});
        return this;
    }
    /** field的值在min和max之间，含min和max， min和max为空则不判断 (格式化)  否则errorMessage*/
    public ModelQueryCheck<T> checkDateRange(String field, Date min,Date max,String format, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"dateRange",new Date[]{min,max},errorMessage,format});
        return this;
    }
    /** field的值在min和max之间，含min和max， min和max为空则不判断  否则errorMessage*/
    public ModelQueryCheck<T> checkNumberRange(String field, Double min,Double max, String errorMessage){
        fieldSet.add(field);
        checkList.add(new Object[]{field,"numberRange",new Double[]{min,max},errorMessage});
        return this;
    }

    /**
     * 执行检查 执行所有检查，存在错误则抛出异常
     */
    public void doCheck() throws Exception {
        doCheck(true,true);
    }
    /**
     * 执行检查 返回错误结果list
     * checkOver 是否检查到最后，isThrow是否抛异常
     */
    public List<String> doCheck(boolean checkOver,boolean isThrow) throws Exception {
        if(fieldSet.isEmpty()) return null;
        if(null==model) {
            model = ModelQueryList.getModel(clazz, oid, fieldSet.toArray(new String[]{}));
        }
        if(null==model) throw new MException(this.getClass(),"实例不存在");
        List<String> errList=new ArrayList<>();
        try {
            for (Object[] ps : checkList) {
                String err = exeCheck(ps, checkOver);
                if (!StringUtil.isSpace(err)) {
                    errList.add(err);
                }
            }
        }catch(Exception e){
            errList.add(e.getMessage());
        }
        if(isThrow&& !errList.isEmpty())
            throw new MException(this.getClass(), ArrayUtil.connection(errList.toArray(new String[]{}),","));
        return errList;
    }

    /**
     * 执行全部检查后 获取model  (传入model的没必要使用该方法)
     */
    public T getModel(String... fields) throws Exception {
        return getModel(true,fields);
    }
    /**
     * 执行检查后 获取model  (传入model的没必要使用该方法)
     * checkOver 是否检查到最后
     */
    public T getModel(boolean checkOver,String... fields) throws Exception {
        this.fieldSet.addAll(Arrays.asList(fields));
        doCheck(checkOver,true);
        return model;
    }

    /**
     * 检查  ps[field,oper,value,errorMessage,format]
     */
    private String exeCheck(Object[] ps,boolean noThrow) throws MException, ClassNotFoundException {
        List<Object> olist=new ArrayList<>();
        if(ps[0] instanceof String[]){
            for(String f : (String[])ps[0])
                olist.add(ActionUtil.getInAttribute(model, f));
        }else{
            olist.add(ActionUtil.getInAttribute(model, (String) ps[0]));
        }
        List<Object> mvList=new ArrayList<>();//获取model的属性值
        for(Object o : olist){
            if(o instanceof Object[]){
                mvList.addAll(Arrays.asList((Object[]) o));
            }else{
                mvList.add(o);
            }
        }
        boolean noErr=false;//默认有误，判断中修改成true
        String oper= (String) ps[1];//判断方法
        Object val=ps[2];//传入的判断值
        try {
            if ("notNull".equals(oper)) {
                noErr = !StringUtil.isSpace(coverString(mvList.toArray(new Object[0])).replaceAll(",",""));
            } else if ("strEq".equals(oper)) {
                boolean b = true;
                String[] vs = val instanceof String[] ? (String[]) val : new String[]{(String) val};
                for (int i = 0; i < mvList.size(); i++) {
                    if (!coverString(mvList.get(i)).equals(vs[i])) {
                        b = false;
                        break;
                    }
                }
                noErr = b;
            } else if ("strLike".equals(oper)) {
                String[] vs = val instanceof String[] ? (String[]) val : new String[]{(String) val};
                String o = coverString(mvList.toArray(new Object[0]));
                if (StringUtil.isSpace(o)) {
                    //属性是空 则不判断
                    noErr = true;
                } else {
                    for (String v : vs) {
                        if (o.contains(v)) {
                            noErr = true;
                            break;
                        }
                    }
                }
            } else if ("strIn".equals(oper)) {
                String[] vs = (String[]) val;
                for (Object f : mvList) {
                    String o = coverString(f);
                    if (StringUtil.isSpace(o)) {
                        //属性是空 则不判断
                        noErr = true;
                        break;
                    } else {
                        if(ArrayUtil.isContain(vs,o)){
                            noErr = true;
                        }
                    }
                }
            } else if ("strAllIn".equals(oper)){
                noErr=true;
                String[] vs = (String[]) val;
                for (Object f : mvList) {
                    String o = coverString(f);
                    if (!StringUtil.isSpace(o)) {
                        if(!ArrayUtil.isContain(vs,o)){
                            noErr = false;
                        }
                    }
                }
            } else if ("strLenRange".equals(oper)) {
                int n = coverString(mvList.toArray(new Object[0])).length();
                Integer[] vs = (Integer[]) val;
                noErr = !(null != vs[0] && vs[0] > n || null != vs[1] && vs[1] < n);
            } else if ("strRegexp".equals(oper)) {
                noErr=true;
                String v = (String) val;
                for (Object f : mvList) {
                    String o = coverString(f);
                    if(!StringUtil.isSpace(o)) {
                        if (!Pattern.matches(v,o)) {
                            noErr = false;
                        }
                    }
                }
            } else if ("dateEq".equals(oper)) {
                String f = (String) ps[4];
                noErr = null != mvList.get(0) && DateUtil.format((Date) val, f).equals(DateUtil.format((Date) mvList.get(0), f));
            } else if ("dateRange".equals(oper)) {
                Date d= (Date) mvList.get(0);
                if(null!=d) {
                    String f = (String) ps[4];
                    Date[] vs = (Date[]) val;
                    noErr=DateUtil.isContain(d,null!=vs[0]?vs[0]:d,null!=vs[1]?vs[1]:d,f);
                }
            } else if("numberRange".equals(oper)){
                double d=Double.parseDouble(coverString(mvList.get(0)));
                Double[] vs = (Double[]) val;
                noErr = !(null != vs[0] && vs[0] > d || null != vs[1] && vs[1] < d);
            }
        }catch (Exception e){
            SystemUtil.println("ModelQueryCheck检查异常：",e.getMessage());
            e.printStackTrace();
        }
        if(noErr){
            return null;
        }else if(noThrow){
            return (String)ps[3];
        }else{
            throw new MException(this.getClass(),(String)ps[3]);
        }
    }
    private String coverString(Object obj){
        if(null==obj) {
            return "";
        }else if(obj instanceof Object[]){
            return ArrayUtil.connection((Object[])obj,",");
        }else if(obj instanceof Date){
            return DateUtil.format((Date) obj, DateUtil.YYYY_MM_DD_HH_MM_SS);
        }else{
            return obj.toString();
        }
    }

    public static void main(String[] a) throws Exception {
        AdminLogin admin=ModelQueryCheck.init(AdminLogin.class,"1")
                .checkNotNull("adminGroup.name","11111111")
                .checkStrEq("oid","1","22222222")
                .checkStrLike("adminGroup.name","管1","4444444")
                .checkStrIn("adminGroup.oid",new String[]{"1","2","3"},"5555555555")
                .checkStrLenRange("realname",null,2,"1235")
                .checkStrRegexp("username",".*d.*","sssss")
                .checkDateEq("lastLoginTime",new Date(),"yyyy-MM-dd","3333333")
                .checkDateRange("lastLoginTime",null,new Date(),"yyyy-MM-dd","22342222")
                .checkStrEq("loginCount","37","vvvv")
                .checkNumberRange("loginCount",37.0,38.0,"mmmm")
                .getModel(true);
        SystemUtil.println(admin);
    }

}
